package breeze.framework.word;

import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFFooter;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

/**
 * @author breeze
 * @date 2023-3-14
 * 不支持word2003,支持2007及以上
 */
public class WordToolkit {
	
	public static WordToolkit toolkit = new WordToolkit();
	
	public static String getWholeText(String localPath) {
		return getWholeText(toolkit.new World2007Plus(localPath));
	}
	
	/** 获取word文档中的所有文字,包含表格,页眉页脚中的文字 */
	public static String getWholeText(World2007Plus word) {
		
		StringBuffer sbf = new StringBuffer();
		/**
		 * 1.处理页眉中的文字,因为内容一致,所以取第一个即可
		 */
		List<XWPFHeader> headers = word.poiDocument.getHeaderList();
		
		if(headers.size() > 0) {
			List<XWPFParagraph> paragraphs = headers.get(0).getParagraphs();
			if(null != paragraphs && paragraphs.size() > 0) {
				for (int i = 0; i < paragraphs.size(); i++) {
					sbf.append(paragraphs.get(i).getText()).append("\n");
				}
			}
		}
		
		/**
		 * 2.处理页脚中的文字,因为内容一致,所以取第一个即可
		 */
		List<XWPFFooter> footers = word.poiDocument.getFooterList();
		if(footers.size() > 0) {
			List<XWPFParagraph> paragraphs = footers.get(0).getParagraphs();
			if(null != paragraphs && paragraphs.size() > 0) {
				for (int i = 0; i < paragraphs.size(); i++) {
					sbf.append(paragraphs.get(i).getText()).append("\n");
				}
			}
		}
		
		/**
		 * 3.处理WORD主体中的文字
		 */
		List<IBodyElement> elements = word.poiDocument.getBodyElements();
		for (int i = 0; i < elements.size(); i++) {
			IBodyElement element = elements.get(i);
			String type = element.getElementType().name();
			if(type.equals("PARAGRAPH")) {
				XWPFParagraph paragraph = (XWPFParagraph) element;
				sbf.append(paragraph.getText()).append("\n");
			}else if(type.equals("TABLE")) {
				XWPFTable table = (XWPFTable) element;
				List<XWPFTableRow> rows = table.getRows();
				for (XWPFTableRow row : rows) {
					// 获取行对应的单元格
					List<XWPFTableCell> cells = row.getTableCells();
					for (XWPFTableCell cell : cells) {
						sbf.append(cell.getText()).append(" ");
					}
					sbf.append("\n");
				}
			}
		}
		return sbf.toString();
	}
	
	/** 替换所有字符串标签 : ${}$或$v{}$ */
	public static void replaceVarTag(World2007Plus word,String source, Object target, String operation) {
		/**
		 * 1.处理页眉中的文字,因为内容一致,所以取第一个即可
		 */
		List<XWPFHeader> headers = word.poiDocument.getHeaderList();
		if(headers.size() > 0) {
			replaceVarTagForParagraph(headers.get(0).getParagraphs(),source,target,operation);
		}
		
		/**
		 * 2.处理页脚中的文字,因为内容一致,所以取第一个即可
		 */
		List<XWPFFooter> footers = word.poiDocument.getFooterList();
		if(footers.size() > 0) {
			replaceVarTagForParagraph(footers.get(0).getParagraphs(),source,target,operation);
		}
		
		/**
		 * 3.处理WORD主体中的文字
		 */
		replaceVarTagForParagraph(word.poiDocument.getParagraphs(),source,target,operation);

		/**
		 * 4.处理WORD表格中的文字.
		 */
		List<XWPFTable> tables = word.poiDocument.getTables();
		if(null != tables && tables.size() > 0) {
			for (int i = 0; i < tables.size(); i++) {
				XWPFTable table = tables.get(i);
				List<XWPFTableRow> rows = table.getRows();
				for (XWPFTableRow row : rows) {
					// 获取行对应的单元格
					List<XWPFTableCell> cells = row.getTableCells();
					for (XWPFTableCell cell : cells) {
						replaceVarTagForParagraph(cell.getParagraphs(),source,target,operation);
					}
				}
			}
		}
	}
	
	public static boolean replaceVarTagForParagraph(List<XWPFParagraph> paragraphs,String source, Object target, String operation) {
		//标记是否在这些段落中找到并替换了source
		boolean hasSource=false;
		//处理段落
		StringBuffer paragraphSbf = new StringBuffer();
		//处理段落中的一段文字
		StringBuffer runSbf = new StringBuffer();
		
		if(null != paragraphs && paragraphs.size() > 0) {
			for (int i = 0; i < paragraphs.size(); i++) {
				XWPFParagraph paragraph = paragraphs.get(i);
				/**
				 * 1.判断此段落中的文字是否包含此变量
				 */
				if(paragraph.getText().indexOf(source) >= 0) {
					
					List<XWPFRun> runs = paragraph.getRuns();//XWPFRun为属性相同的一段区域文字
					//使用前先清空
					runSbf.delete(0, runSbf.length());
					paragraphSbf.delete(0, paragraphSbf.length());
					
					/**
					 * 2.首先正向获取包含变量的所有run的集合
					 */
					for (int j = 0; j < runs.size(); j++) {
						paragraphSbf.append(runs.get(j).toString());
						if(paragraphSbf.indexOf(source) >= 0) {
							/**
							 * 3.再反向获取包含变量的最少run的集合,获取集合后将第一个run的值设为target值,其他run的值设为空.
							 * 找到一个匹配之后,清空StringBuffer,再尝试找此段落中的其他匹配项
							 */
							for (int k = j; k >= 0; k--) {
								XWPFRun run = runs.get(k);
								runSbf.insert(0,run.getText(run.getTextPosition()));
								if(runSbf.indexOf(source) < 0) {
									run.setText("",0);
								}else {
									/**
									 * 4.target有值则直接替换值
									 * 如果target没有值,则在编辑态时标红该变量,再预览态时置空该变量
									 */
									if(null == target || "".equals(target.toString())) {
										if(operation.equals("edit")) {
											run.setText(runSbf.toString(), 0);
											run.setColor("FF0000");
										}else {
											run.setText(runSbf.toString().replace(source, ""), 0);
										}
									}else {
										run.setText(runSbf.toString().replace(source, target.toString()), 0);
									}
									hasSource=true;
									runSbf.delete(0, runSbf.length());
									paragraphSbf.delete(0, paragraphSbf.length());
									break;
								}
							}
						}
					}
				}
			}
		}
		return hasSource;
	}
	
	public static void replaceTableEachColumnTag(World2007Plus word,String source, List<?> value,Map<Integer,Integer> removes,Map<String,String> properties, String operation) {
		
		List<XWPFTable> tables = word.poiDocument.getTables();
		if(null != tables && tables.size() > 0) {
			for (int i = 0; i < tables.size(); i++) {
				XWPFTable table = tables.get(i);
				//1.判断此table中是否含有变量
				if(table.getText().contains(source)) {
					//2.寻找此变量所在的行和列
					List<XWPFTableRow> rows = table.getRows();
					int rowPos = 0;
					int columnPos = 0;
					boolean exist = false;
					for (rowPos = 0; rowPos < rows.size(); rowPos++) {
						XWPFTableRow row = rows.get(rowPos);
						List<XWPFTableCell> cells = row.getTableCells(); 
						for (columnPos = 0; columnPos < cells.size(); columnPos++) {
							XWPFTableCell cell = cells.get(columnPos);
							if(cell.getText().contains(source)) {
								exist = true;
								break;
							}
						}
						if(exist) {
							break;
						}
					}
					
					removes.put(i, rowPos);
					
					//3.判断此table是否需要新增行
					if(null == properties.get(table.toString())) {
						//4.在找到的行下方复制一行
						XWPFTableRow sourceRow = table.getRow(rowPos);
						for (int k = 0; k < value.size(); k++) {
							XWPFTableRow targetRow = table.insertNewTableRow(rowPos + 1 + k);
							targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
							
							List<XWPFTableCell> sourceCells = sourceRow.getTableCells();
							if (null == sourceCells) {
							    continue;
							}
							
							//复制列及其属性和内容
							XWPFTableCell targetCell = null;
							for (int l = 0; l < sourceCells.size(); l++) {
								XWPFTableCell sourceCell = sourceCells.get(l);
								targetCell = targetRow.addNewTableCell();
								targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
								
						        List<XWPFParagraph> sourceParagraphs = sourceCell.getParagraphs();
						        if (CollectionUtils.isEmpty(sourceParagraphs)) {
						            continue;
						        }
						        XWPFParagraph sourceParagraph = sourceParagraphs.get(0);
						        List<XWPFParagraph> targetParagraphs = targetCell.getParagraphs();
						        if (CollectionUtils.isEmpty(targetParagraphs)) {
						            XWPFParagraph p = targetCell.addParagraph();
						            p.getCTP().setPPr(sourceParagraph.getCTP().getPPr());
						            XWPFRun run = p.getRuns().isEmpty() ? p.createRun() : p.getRuns().get(0);
						            run.setFontFamily(sourceParagraph.getRuns().get(0).getFontFamily());
									run.setBold(sourceParagraph.getRuns().get(0).isBold());
									run.setFontSize(sourceParagraph.getRuns().get(0).getFontSizeAsDouble());
						        } else {
						            XWPFParagraph p = targetParagraphs.get(0);
						            p.getCTP().setPPr(sourceParagraph.getCTP().getPPr());
						            XWPFRun run = p.getRuns().isEmpty() ? p.createRun() : p.getRuns().get(0);
						            run.setFontFamily(sourceParagraph.getRuns().get(0).getFontFamily());
						            run.setBold(sourceParagraph.getRuns().get(0).isBold());
									run.setFontSize(sourceParagraph.getRuns().get(0).getFontSizeAsDouble());
						        }
							}
						}
						properties.put(table.toString(), "inserted");
					}

					for (int k = 0; k < value.size(); k++) {
						Object val = value.get(k);
						if(null == val || "".equals(val.toString())) {
							if(operation.equals("edit")) {
								if(k == 0) {
									table.getRow(rowPos + 1 + k).getCell(columnPos).getParagraphs().get(0).getRuns().get(0).setText(source);
									table.getRow(rowPos + 1 + k).getCell(columnPos).getParagraphs().get(0).getRuns().get(0).setColor("FF0000");

								}
							}
						}else {
							table.getRow(rowPos + 1 + k).getCell(columnPos).getParagraphs().get(0).getRuns().get(0).setText(val.toString());
						}
					}
				}
			}
		}
	}
	
	public static void replaceTableEachTag(World2007Plus word,String tableTag,List<Map<String,Object>> tableData){
		String start = tableTag.replace("}$", "") + ".";
		Map<String,List<Object>> group = new LinkedHashMap<String,List<Object>>();
		for (int i = 0; i < tableData.size(); i++) {
			LinkedHashMap<String,Object> row = (LinkedHashMap<String, Object>) tableData.get(i);
			for (String key : row.keySet()) {
				String source = start + key + "}$";
				List<Object> columns;
				if(group.containsKey(source)) {
					columns = group.get(source);
				}else {
					columns = new ArrayList<Object>();
					group.put(source, columns);
				}
				columns.add(row.get(key));
			}
		}
		//用于生成表格行后移除变量所在的行
		Map<Integer,Integer> removes = new HashMap<Integer,Integer>();
		//用于判断是否其他列已经生成了所需的行
		Map<String,String> properties = new LinkedHashMap<String,String>();

		for (String source : group.keySet()) {
			replaceTableEachColumnTag(word,source,group.get(source),removes,properties,"edit");
		}
		
		if(removes.keySet().size() > 0) {
			for (Integer key : removes.keySet()) {
				word.poiDocument.getTableArray(key).removeRow(removes.get(key));
			}
		}
	}
	
	public static void saveTo(World2007Plus word,String targetPath){
		try {
			FileOutputStream out = new FileOutputStream(targetPath);
			word.poiDocument.write(out);
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		word.close();
	}
	
	public class World2007Plus implements Closeable{
		
		private String localPath;
		private XWPFDocument poiDocument;
		private FileInputStream inputStream;
		//包含页眉,页脚在内的所有段落(不包含表格).
		private List<XWPFParagraph> paragraphsss = new ArrayList<XWPFParagraph>();
		
		public World2007Plus(String localPath) {
			this.localPath = localPath;
			try {
				inputStream = new FileInputStream(localPath);
				poiDocument = new XWPFDocument(inputStream);//默认初始化POI
			} catch (Exception e) {
				//文件格式错误或错误版本
				e.printStackTrace();
			}
		}
		
		public String getLocalPath() {
			return localPath;
		}
		public void setLocalPath(String localPath) {
			this.localPath = localPath;
		}
		public XWPFDocument getPoiDocument() {
			return poiDocument;
		}
		public void setPoiDocument(XWPFDocument poiDocument) {
			this.poiDocument = poiDocument;
		}
		public FileInputStream getInputStream() {
			return inputStream;
		}
		public void setInputStream(FileInputStream inputStream) {
			this.inputStream = inputStream;
		}
		public List<XWPFParagraph> getParagraphsss() {
			return paragraphsss;
		}

		public void setParagraphsss(List<XWPFParagraph> paragraphsss) {
			this.paragraphsss = paragraphsss;
		}
		public void close() {
			try {
				poiDocument.close();
				inputStream.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		@SuppressWarnings("serial")
		Map<String, Object> maps = new HashMap<String,Object>(){{
			put("${乡镇/街道}$", "金清镇");
			put("${0}$", "1000");
			put("${1}$", "1001");
			put("${2}$", "1002");
			put("${3}$", "1003");
			put("${4}$", "1004");
			put("${5}$", "1005");
			put("${6}$", "1006");
			put("${7}$", "1007");
			put("${上升名/下降X名/排名无变化}$", "上升5名");
			put("${8}$", "1008");
			put("${9}$", "1009");
			put("${10}$", "1010");
			put("${11}$", "1011");
			put("${12}$", "1012");
			put("$e{基础数据表格}$", new ArrayList<LinkedHashMap<String,Object>>(){{
				add(new LinkedHashMap<String,Object>(){{
					put("项目", "溢流井(动态生成)");
					put("上月", "已整治7，总数35(动态生成)");
					put("本月", "已整治7，总数35(动态生成)");
				}});
				add(new LinkedHashMap<String,Object>(){{
					put("项目", "10个乡镇街道总排名(动态生成)");
					put("上月", "第5(动态生成)");
					put("本月", "第6(动态生成)");
				}});
			}});
		}};
		
//		World2007Plus word = toolkit.new World2007Plus("C:\\Users\\breeze\\Desktop\\XX乡镇街道月度年度报告初稿-模板.docx");
		World2007Plus word = toolkit.new World2007Plus("D:\\Work\\若依\\tzsw_java\\ruoyi-common\\ruoyi-common-core\\src\\main\\java\\breeze\\framework\\word\\XX乡镇街道月度年度报告初稿-模板.docx");
		for (String key : maps.keySet()) {
			if(key.startsWith("${") || key.startsWith("$v{")) {
				replaceVarTag(word,key,maps.get(key),"edit");
			}else if(key.startsWith("$e{") || key.startsWith("$each{")) {
				replaceTableEachTag(word,key,(List<Map<String,Object>>)maps.get(key));
			}
		}
		System.out.println(getWholeText(word));
//		saveTo(word,"C:\\Users\\breeze\\Desktop\\XX乡镇街道月度年度报告初稿.docx");
		saveTo(word,"D:\\Work\\若依\\tzsw_java\\ruoyi-common\\ruoyi-common-core\\src\\main\\java\\breeze\\framework\\word\\XX乡镇街道月度年度报告初稿-模板.docx");
	}
	
}
